#include <iostream>
#include <fstream>
#include <string>
#include "stdafx.h"
#include "..\database\database.h"

using namespace std;

const int MAX_CHARS_IN_LINE=80;

const int nTokens=17;
const string TokenArray[]=
{
  "SELECT",
  "FROM",
  "WHERE",
  "CREATE",
  "DELETE",
  "UPDATE",
  "SET",
  "DEFINE",
  "INSERT",
  "WITH",
  "SHOW",
  "SHOWALL",
  "LOADSCHEMA",
  "LOADDATA",
  "SAVESCHEMA",
  "SAVEDATA",
  "HELP"
};

typedef enum TokenType{
	selectType,
	fromType,
	whereType,
	createType,
	deleteType,
	updateType,
	setType,
	defineType,
	insertType,
	withType,
	showType,
	showallType,
  loadType,
  loaddataType,
  saveType,
  savedataType,
  helpType,
	invalidType=-1
};
  

void ListCommands();
void Parse(string c,Database&d);
TokenType isToken(string t);

int _tmain(int argc, _TCHAR* argv[])
{
  cout << "\nEnter Command (HELP for list of commands, QUIT to exit)\n";
  Database dbase;
  string command;
  do{	
	  //This loop takes query commands and partially parses them.
	  //The results are used by the appropriate methods.
    cin.clear();
	  cout << "\n> ";
    do{
      getline(cin,command);
    }while(command.length()==0);
	  if(command=="QUIT")
		  return 0;
	  //Split up the query and call the corresponding functions
		Parse(command,dbase);		
  }while(true);
}

// LISTS THE COMMANDS IMPLEMENTED. HELP COMMAND DISPLAYS THIS LIST.
void ListCommands(){
	system("cls");
	cout << "\t\t\tLIST OF COMMANDS"<<endl;
	cout << "All commands, tablenames and attribute names are CASE SENSITIVE " << endl << endl;
	cout << "To Load Schema from FILE  ->  LOADSCHEMA <FileName>" << endl << endl;
	cout << "To Load Record Information from FILE  ->  LOADDATA <FileName>" << endl << endl;
	cout << "To Save the Schema(Table Information) to a FILE  -> SAVESCHEMA <FileName>" << endl << endl;
	cout << "To Save the Record Information to a FILE  ->  SAVEDATA <FileName>" << endl << endl;
	cout << "Create a Table -> DEFINE <TableName> WITH <AttributeType> <AttributeName>,..." << endl;
	cout << "To specify primary key - prefix the attribute name with *" << endl << endl;
	cout << "Delete a Table -> DELETE <TableName>" << endl << endl;
	cout << "Insert a Record -> INSERT <TableName> WITH <value>, <value>,..." << endl;
  cout << "Note: If <value> is STRING should be enclosed in single quotes('')" << endl << endl;
  cout << "To display contents of a Table  ->  SHOW <TableName>"<<endl<<endl;
	cout << "To display contents of all Tables  ->  SHOWALL"<<endl<<endl;
	cout << "Delete a Record -> DELETE FROM <TableName> WHERE <Condition>" << endl << endl;
	cout << "Empty a Table -> DELETE FROM <TableName>" << endl << endl;
	cout << "Update a Record -> UPDATE <TableName> SET <AttributeName> = <Value>,... WHERE <Condition>" << endl;
  cout << "Note: If <value> is STRING should be enclosed in single quotes('')" << endl << endl;
	cout << "Querying the Database -> SELECT <AttributeName>, ... FROM <TableName> WHERE <Condition>" << endl;
  cout <<endl<<endl<< "Press any key to continue.... ";

	_getch();
	system("cls");
  cout << "\nEnter Command (HELP for list of commands, QUIT to exit)\n";
}

//THIS FUNCTION TAKES A COMMAND AND BREAKS IT INTO PIECES TO BE SENT TO REQUESTED FUNCTIONS
void Parse(string command,Database&d){

  vector<string> tokens;
  vector<string> clauses;
  string t, TableName;

  stringstream s, stream;	//put the command into a stringstream
  s << command;
  s >> t;			//take out the first 'next' string
  do{					//this loop gets a token followed by the corresponding clause
    if(isToken(t)==-1){	//initially we expect to find a token
      cerr << "There is some sort of error in the input\n";
	  return;
    }else{
      tokens.push_back(t);	//put the token into the vector

      string c="";				//then we look for the next token
      string p;
      int gotToken=0;
      int count=0;
      do{
        s >> p;
        gotToken=isToken(p);
        if(gotToken==-1){	//we only add strings which are not tokens to the next clause
          if(count==0){
            c=p;
          }else{
            c=c+" "+p;
          }
        }else{
          t=p;				//if we find a token, we loop again
        }
        ++count;
      }while(gotToken==-1 && !s.eof());
      clauses.push_back(c);
    }
  }while(!s.eof());
  
  switch(isToken(tokens[0])){
	  case updateType:
		  d.UpdateTable(clauses[0],clauses[1],clauses[2]);
		  break;
	  case selectType:
      //Command in the form of SELECT FROM WHERE or SELECT FROM
		  switch(clauses.size()){
			case 2:
				d.PrintRecordInfo(d.QueryObj.RemoveSpace(d.QueryObj.doQuery(clauses[1],"",clauses[0])),cout);
				break;
			case 3:
				d.PrintRecordInfo(d.QueryObj.RemoveSpace(d.QueryObj.doQuery(clauses[1],clauses[2],clauses[0])),cout);
				break;
			}
		  break;
	  case deleteType:
      // Command in the form DELETE FROM WHERE
			if(clauses.size()==3){
					d.DeleteRecord(clauses[1], clauses[2]);
					cout << "The Records that matched were deleted" << endl;
			}
			//Command in the form of DELETE
			else if(clauses.size()==1){
					d.DeleteTable(clauses[0]);
					cout << "The Table was deleted" << endl;
			}
			//Command in the form of DELETE FROM
			else if(clauses.size()==2){
			  d.EmptyTable(clauses[1]);
				cout << "The Table " << clauses[1] << " was emptied." << endl;
			}
		  break;
	  case defineType:
		  //command in the form of DEFINE <TableName> WITH  <AttribteType> <AttributeName>, ...
		  d.DefineWith(clauses[0], clauses[1]);
		  break;
	  case showType: 
		  //command SHOW <TableName>
		  d.PrintRecordInfo(d.QueryObj.RemoveSpace(clauses[0]),cout);
		  break;
	  case showallType:
      d.PrintAllTableRecord(cout);
			break;
	  case insertType:
		  //Command in the form of INSERT <TableName> WITH <value>, <value>,...
		  TableName = d.QueryObj.RemoveSpace(clauses[0]);
		  d.InsertRecord(TableName, clauses[1]);
		  break;
    case loadType:
      //Command in the form of LOADSCHEMA <filename>
      d.DefineDatabaseFromFile(clauses[0]);
      break;
    case loaddataType:
      //Command in the form of LOADDATA <filename>
      d.InsertDataFromFile(clauses[0]);
      break;
    case saveType:
      //Command in the form of SAVESCHEMA <filename>
      d.SaveTable(clauses[0]);
      break;
    case savedataType:
      //Command in the form of SAVEDATA <filename>
      d.SaveData(clauses[0]);
      break;
    case helpType:
      //Comand in the form of HELP
	    ListCommands();
      break;
    default:
		  cerr << "Invalid command\n";
  }
  return;
}

//Checks to see if str is a token in TokenArray
TokenType isToken(string str){
  for(int x=0;x<nTokens;++x)
    if(str==TokenArray[x])
	  //this is token x
      return TokenType(x);		
  //this is not a recognized token
  return invalidType;		
}